#include "rewine/PathResolver.hpp"

#include "misc/fs_ext.hpp"
#include "misc/string_ext.hpp"
#include "rewine/LoggerSingleton.hpp"

#include <algorithm>

using std::string;

namespace rewine
{

static inline std::string
ensure_path_with_delimiter(std::string path)
{
    std::string s(std::move(path));
    if (!ends_with(s, "/")) {
        s.append("/");
    }
    return s;
}

int
PathResolver::add(string path)
{
    path = resolve_path(path);
    if (contains(path)) {
        return -1;
    }
    path = ensure_path_with_delimiter(std::move(path));
    g_Logger->debug(__FLF__, "add path {}", path.c_str());
    m_Dirs.emplace_back(std::move(path));
    return 0;
}

int
PathResolver::remove(string path)
{
    path = resolve_path(path);
    path = ensure_path_with_delimiter(std::move(path));
    auto it = std::find_if(m_Dirs.begin(), m_Dirs.end(), [&path](const string& dir) -> bool {
        return path == dir;
    });
    if (it == m_Dirs.end()) {
        return -1;
    }
    g_Logger->debug(__FLF__, "remove path {}", path.c_str());
    m_Dirs.erase(it);
    return 0;
}

bool
PathResolver::contains(std::string path)
{
    path = resolve_path(path);
    auto path0 = ensure_path_with_delimiter(std::move(path));
    for (const auto& dir : m_Dirs) {
        if (path0 == dir) {
            return true;
        }
    }
    return false;
}

static std::string
try_files(const std::string& fullname)
{
    std::string orig = fullname;
    if (is_file_exist(orig, true)) {
        return orig;
    }
    if (ends_with_ignore_case(fullname, ".dll")) {
        string dllso = fullname + ".so";
        if (is_file_exist(dllso, true)) {
            g_Logger->trace(__FLF__, "found {}.so instead", fullname);
            return dllso;
        }
    }
    return string();
}

std::string
PathResolver::resolve(std::string filename)
{
    if (starts_with(filename, "/")) {
        // absolute path
        return try_files(filename);
    }
    // relative or filename only
    for (const auto &dir : m_Dirs) {
        auto fullname = try_files(dir + filename);
        if (!fullname.empty()) {
            g_Logger->debug(__FLF__, "resolve {} as {}", filename, fullname);
            return fullname;
        }
    }
    g_Logger->info(__FLF__, "resolve {}: failed", filename);
    return string();
}

}; // namespace rewine
